﻿using Dycox.Workflows.Manipulation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Dycox.Workflows
{
    public class ModifyStageContext : WorkflowExecutionContext
    {
        Stack<ContextState> _stack;

        internal ModifyStageContext(Workflow wf, OperationArguments args)
            : base(wf, args)
        {
            var id = args.ComponentID;
            if (id.StepSequence == null)
                throw new ArgumentException(SR.GetString("error_not_a_step"));

            _stack = new Stack<ContextState>(10);
        }

        protected override IWorkflowComponent GetContextComponent()
        {
            return DataProvider.GetComponent(Arguments.ComponentID, true);
        }

        public StepContext Modifying
        {
            get { return State.Current; }
        }

        public StageOperationContract OriginalState
        {
            get
            {
                return State.OriginalContract;
            }
        }

        public StepContext Previous
        {
            get { return State.Previous; }
        }

        private ContextState State
        {
            get
            {
                if (_stack.Count == 0)
                    throw new WorkflowException(SR.GetString("error_context_not_initialized"));
                return _stack.Peek();
            }
        }

        internal void PushState(WFStep current, WFStep previous)
        {
            StepContext modifying = new StepContext(current, this.Workflow);
            StepContext p;

            if (previous != null)
                p = new StepContext(previous, this.Workflow);
            else
                p = null;

            _stack.Push(new ContextState(p, modifying));
        }

        internal void PopState()
        {
            _stack.Pop();
        }

        public StageOperationContract GetContract()
        {
            var step = this.Modifying;
            return step.Stage.GetContract(step.Step);
        }

        public void UpdateContract(StageOperationContract contract)
        {
            if (contract == null)
                throw new ArgumentNullException("contract");

            var step = Modifying.Step;

            Modifying.Stage.ValidateContract(contract, this);

            step.SetUserState(contract);
            step.LastModifyTime = WorkflowManager.TimeService.Now;
            step.Comment = contract.Comment;
            step.OperatorName = contract.OperatorName;
        }

        public bool IsModificationSource
        {
            get
            {
                return Modifying.Step == this.Step;
            }
        }

#if DEBUG
        internal bool StackIsEmpty
        {
            get
            {
                return _stack.Count == 0;
            }
        }
#endif

        class ContextState
        {
            public ContextState(StepContext previous, StepContext current)
            {
                if (current == null)
                    throw new ArgumentNullException("current");

                _previous = previous;
                _current = current;
            }

            private StepContext _previous;

            public StepContext Previous
            {
                get { return _previous; }
            }

            private StepContext _current;

            public StepContext Current
            {
                get { return _current; }
            }

            private StageOperationContract _originalContract;

            public StageOperationContract OriginalContract
            {
                get
                {
                    if (_originalContract == null)
                        _originalContract = Current.Stage.GetContract(Current.Step);

                    return _originalContract;
                }
            }
        }
    }
}
